package keer.httpinterface.proxy;

import keer.httpinterface.HttpInterfaceException;
import keer.httpinterface.http.HttpBody;
import keer.httpinterface.http.HttpContentType;
import keer.httpinterface.http.HttpParams;
import keer.httpinterface.utils.ClassUtils;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 请求体读取器，用于匹配不同的Content-Type，将参数转换为请求体
 * @see HttpBody
 * @see HttpContentType
 * @see keer.httpinterface.http.HttpRequest
 */
public abstract class ContentTypeBodyReader {
    private final static ConcurrentHashMap<String, ContentTypeBodyReader> READER_CACHE_MAP = new ConcurrentHashMap<>();

    /**
     * 读取请求体，由子类实现，根据不同的Content-Type，将参数转换为请求体
     * @param contentType Content-Type请求头
     * @param params 请求参数
     * @return 请求体
     * @throws Exception 抛出转换过程中的任何异常
     */
    protected abstract HttpBody read0(HttpContentType contentType, HttpParams params) throws Exception;

    /**
     * 子类所支持的Content-Type
     * @return Content-Type请求头（type/subType）
     */
    protected abstract String contentType();

    static {
        try {
            List<Class<ContentTypeBodyReader>> classes = ClassUtils.getSubClassesByPackage(ContentTypeBodyReader.class.getPackage().getName(), ContentTypeBodyReader.class);
            for (Class<ContentTypeBodyReader> clazz : classes) {
                ContentTypeBodyReader reader = clazz.newInstance();
                READER_CACHE_MAP.put(reader.contentType().toLowerCase(), reader);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 读取请求体, 根据Content-Type匹配不同的读取器，将{@link HttpParams}形式的请求参数转换成{@link HttpBody}
     * @param contentType Content-Type请求头
     * @param params 请求参数
     * @return 请求体
     * @throws HttpInterfaceException 抛出转换过程中的任何异常
     */
    public static HttpBody read(HttpContentType contentType, HttpParams params) throws HttpInterfaceException {
        ContentTypeBodyReader reader = READER_CACHE_MAP.get(contentType.contentType().toLowerCase());
        if (reader == null) {
            throw new HttpInterfaceException("Unsupported content type: " + contentType);
        }
        try {
            return reader.read0(contentType, params);
        } catch (Exception e) {
            throw new HttpInterfaceException("Read request body failure.", e);
        }
    }
}
